<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        :root {
            --btn-w: 40px;
            --btn-h: 24px;
            /* 按钮伪元素 */
            --btn-dot-w: 4px;
            /* 滑块 */
            --bar-h: 10px;
            /* 背景图 */
            --pic-w: 640px;
            --pic-h: 390px;
            --pic-src: url(https://i0.hdslb.com/bfs/vc/c13315f4c4195b342fd0d2795fd6c8b090a717bf.jpg);
            --radius: 8px;
        }

        * {
            margin: 0;
            padding: 0;
        }

        .container {
            display: flex;
            position: relative;
            height: var(--pic-h);
            width: var(--pic-w);
            flex-flow: column wrap;
            justify-content: space-between;
            padding: 10px;
        }

        .pic {
            position: relative;
            background: var(--pic-src) no-repeat;
            width: 640px;
            height: 340px;
        }

        .slide {
            position: relative;
            width: 100%;
            height: var(--bar-h);
            background-color: #999;
            border-radius: 4px;
        }

        .btn {
            position: absolute;
            left: 0;
            /* (按钮高度 - 拖动条高度) * -1 / 2 */
            top: calc((var(--btn-h) - var(--bar-h)) * -1 / 2);
            width: var(--btn-w);
            height: var(--btn-h);
            background-color: #b5a37e;
            border-radius: 10px;
            cursor: pointer;
        }

        .btn::after,
        .btn::before {
            content: "";
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            left: calc(var(--btn-w) / 3 - var(--btn-dot-w) / 2);
            height: var(--bar-h);
            width: var(--btn-dot-w);
            background-color: #eee;
        }

        .btn::before {
            /* 伪元素在按钮的 2/ 3处 并减去自己的一半用来居中 */
            left: calc(var(--btn-w) / 3 * 2 - var(--btn-dot-w) / 2);
        }

        .verify-pic {
            position: absolute;
            display: none;
            left: 0;
            top: 0;
            box-shadow: 1px 2px 8px #ddd;
            width: calc(var(--pic-w) / 7);
            height: calc(var(--pic-h) / 5);
            background: var(--pic-src) no-repeat -100px -50px;
            border-radius: var(--radius);
        }

        .gap {
            position: absolute;
            left: 0;
            top: 0;
            width: calc(var(--pic-w) / 7);
            height: calc(var(--pic-h) / 5);
            background-color: #eee;
            border-radius: var(--radius);
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="pic">
            <div class="gap"></div>
            <div class="verify-pic"></div>
        </div>
        <div class="slide">
            <div class="btn"></div>
        </div>
    </div>

    <script type="text/javascript">
        const container = document.querySelector('.container'),
            pic = container.querySelector('.pic'), // 大图
            vPic = pic.querySelector('.verify-pic'), // 拖动图片
            gap = pic.querySelector('.gap'), // 背景图空白块
            btn = document.querySelector('.btn'); // 滑动条按钮

        const pic_w = getStyle(pic, 'width'),
            pic_h = getStyle(pic, 'height'),
            cont_w = getStyle(container, 'width'),
            cont_h = getStyle(container, 'height'),
            vPic_w = getStyle(vPic, 'width'),
            vPic_h = getStyle(vPic, 'height'),
            btn_w = getStyle(btn, 'width');

        const offset = 14;  // 可偏移距离


        init(() => {
            console.log('验证通过');
        });


        /**
         * 接收一个回调函数 验证通过执行
         */
        function init(cb) {
            const [left, top] = setPos();
            bindEvent(left, top, cb);
        }

        function setPos() {
            const w = pic_w / 2,
                h = pic_h / 2 - vPic_h;

            // 移动空缺元素到右上部分
            const left = getRadom(w, pic_w - vPic_w),
                top = getRadom(0, h);
            gap.style.transform = `translate(${left}px, ${top}px)`;
            vPic.style.backgroundPosition = `${-left}px ${-top}px`;

            return [left, top];
        }

        function bindEvent(left, top, cb) {
            let x = 0,
                moving = false;
            btn.addEventListener('mousedown', function (e) {
                // 不阻止默认行为 会导致鼠标属性变成 `now-allowed`
                e.preventDefault();
                moving = true;
                setShow(top, 'block');
            });
            window.addEventListener('mouseup', function () {
                const flag = x > left - offset && x < left + offset;
                
                moving = false;
                if (flag) {
                    cb && cb();
                }
                else {
                    setShow(top, 'none');
                }
            });

            window.addEventListener('mousemove', function (e) {
                if (!moving || judge(e.clientX)) {
                    return;
                }

                // 图片位置 = 鼠标位置 - 滑动条位置 - 按钮 / 2 ----减去按钮是居中
                x = e.clientX - container.getBoundingClientRect().left - btn_w / 2;
                btn.style.transform = `translateX(${x}px)`;
                vPic.style.transform = `translate(${x}px, ${top}px)`;
            });
        }

        function setShow(top, flag) {
            vPic.style.display = flag;
            vPic.style.transform = `translateY(${top}px)`;

            btn.style.transform = 'none';
            vPic.style.transform = 'none';
            x = 0;
        }

        function judge(x) {
            const { left } = container.getBoundingClientRect();
            return (
                x - left < 0 ||
                x + left > cont_w + left
            );
        }

        function getRadom(min, max) {
            return Math.floor(min + Math.random() * (max - min));
        }

        function getStyle(el, key) {
            return parseInt(getComputedStyle(el)[key]);
        }
    </script>
</body>

</html>